require 'rexml/document'  # ruby stdlib

module SitemapSpider

  # sitemap.xml sample
  # mandatory: urlset, url, loc
  #
  # <?xml version="1.0" encoding="UTF-8"?>
  # <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  #   xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9"
  #   url="http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"   
  #   xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
  #   xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
  #   <url>
  #     <loc>http://www.example.com/</loc>
  #     <lastmod>2005-01-01</lastmod>
  #     <changefreq>monthly</changefreq>
  #     <priority>0.8</priority>
  #     <image:image>
  #       <image:loc>http://example.com/obrazek.jpg</image:loc>
  #       <image:caption>Description of image</image:caption>
  #       <image:geo_location>Prague, Czech Republic</image:geo_location>
  #       <image:title>Nice image title</image:title>
  #       <image:license>no license</image:license>
  #     </image:image>       
  #   </url>  
  # </urlset> 
  #
  class Sitemap

    private

    def get_sitemap_document
      if File.exist?(@opts[:output_file])
        @xml = REXML::Document.new(File.open(@opts[:output_file]))
      else
        xml = REXML::Document.new.add(REXML::XMLDecl.new('1.0', 'UTF-8'))
        xml.add_element('urlset', 'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
          'xmlns:image' => 'http://www.google.com/schemas/sitemap-image/1.1',
          'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
          'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd')
        xml.root.context[:attribute_quote] = :quote
        @xml = xml
      end
    end

    def update_element(parent_element, smrec)
      delete_update_leave = (@opts[:delete_old_attributes] ? :update_or_delete_subelement : :update_or_leave_subelement)
      if parent_element
        update_image_subelements(parent_element, smrec)
        attrs_to_be_updated = smrec.attributes
        # update elements which are already there
        parent_element.each_element do |e|
          self.send(delete_update_leave, e, smrec)
          attrs_to_be_updated.delete(e.name.to_sym)
        end
        # add new attributes
        attrs_to_be_updated.each do |a|
          parent_element.add_element(smrec.attribute_to_element(a))
        end
      else
        @xml.root.add_element(smrec.to_url_element)
      end
    end

    def update_image_subelements(parent_element, smrec)
      # XXX fixme: now we always delete old image attributes, in future we should respect @opts[:delete_old_attributes]
      parent_element.each_element('image:image') do |img_element|
        img_element.parent.delete(img_element)
      end
      smrec.images.each do |img|
        parent_element.add_element(img.to_image_element)
      end
    end

    def update_or_delete_subelement(element, smrec)
      if smrec.respond_to?(element.name.to_sym) and smrec.send(element.name.to_sym)
        element.text = smrec.send(element.name.to_sym)
      else
        element.parent.delete(element)
      end
    end

    def update_or_leave_subelement(element, smrec)
      if smrec.respond_to?(element.name.to_sym) and smrec.send(element.name.to_sym)
        element.text = smrec.send(element.name.to_sym)
      end
    end

    def update_sitemap_xml(rec, opts = {})
      records = rec.dup
      while records.size > 0 do
        smrec = records.shift
        # parent_element is the 'url' element of the searched-for 'loc' element
        parent_element = nil
        to_be_deleted = []
        @xml.root.each_element_with_text(smrec.loc, 0, 'url/loc') do |loc_element|
          if parent_element
            # delete duplicate elements
            to_be_deleted << loc_element.parent
          else
            parent_element = loc_element.parent
          end
        end
        to_be_deleted.each {|d| d.parent.delete(d)}
        if opts[:delete]
          @xml.root.delete(parent_element)
        else
          update_element(parent_element, smrec)
        end
      end
    end

    def write_out_xml
      File.open(@opts[:output_tmp_file], 'w') {|f| REXML::Formatters::Default.new.write(@xml, f)}
      File.rename(@opts[:output_tmp_file], @opts[:output_file])
    end
  end
end
